﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using ABB.Common.DataModel;
using ABB.Common.BusinessLogic.Helper;
using ABB.Common.BusinessLogic.Contracts;

namespace ABB.Common.BusinessLogic.Reporting
{
    /// <summary>
    /// Ist zuständig für das maximal optimierte Laden von Daten für Daten auf Lots
    /// über Report Spalten.
    /// </summary>
    internal class ReportDataLoader : IReportDataLoader
    {
        private ProjectPlanningDataSet m_DataSet;
        private IChangeLogModule m_ChangeLogModule;

        /// <summary>
        /// Initialisiert den DataLoader mit dem DataSet, aus welchem
        /// die Daten geladen werden sollen.
        /// </summary>
        /// <param name="dataSet"></param>
        public ReportDataLoader(ProjectPlanningDataSet dataSet, IChangeLogModule changeLogModule)
        {
            this.m_DataSet = dataSet;
            this.m_ChangeLogModule = changeLogModule;
        }

        #region IReportDataLoader Members

        public ProjectPlanningDataSet DataSet
        {
            get { return this.m_DataSet; }
        }

        /// <summary>
        /// Extrahiert den entsprechenden Wert für den Lot gemäss der übergebenen
        /// <see cref="ProjectPlanningDataSet.ReportColumnRow"/>.
        /// </summary>
        /// <param name="lot"></param>
        /// <param name="column"></param>
        /// <param name="user">Der User, benötigt für das filtern der ChangeLogs.</param>
        /// <param name="hasChanged">Output Parameter, gibt an ob der Wert sich für den Benutzer
        /// verändert hat.</param>
        /// <returns></returns>
        public string GetValueForLotByReportColumn(ProjectPlanningDataSet.LotRow lot,
                                            ProjectPlanningDataSet.ReportColumnRow column,
                                            ProjectPlanningDataSet.UserRow user, out bool hasChanged)
        {
            hasChanged = false;

            switch ((ReportColumnType)column.Type)
            {
                case ReportColumnType.Label:
                    return this.LoadLabelDataForLotAndColumn(lot, column, user, out hasChanged);
                case ReportColumnType.MilestoneCompletion:
                case ReportColumnType.MilestoneInitialDate:
                case ReportColumnType.MilestonePlanDate:
                case ReportColumnType.MilestoneType:
                    return this.LoadMilestoneDataForLotAndColumn(lot, column, user, out hasChanged);
                case ReportColumnType.ChangeColumn:
                    return this.m_ChangeLogModule.HasReportRowChangesByUser(column.ReportRow, lot, user).ToString();
                case ReportColumnType.CommentColumn1:
                case ReportColumnType.CommentColumn2:
                case ReportColumnType.CommentColumn3:
                    return string.Empty;
            }

            throw new ApplicationException("Can not resolve value for row.");
        }

        /// <summary>
        /// Extrahiert den entsprechenden Wert für den Lot gemäss der übergebenen
        /// <see cref="ProjectPlanningDataSet.ReportColumnRow"/>.
        /// </summary>
        /// <param name="lot"></param>
        /// <param name="column"></param>
        /// <returns></returns>
        public string GetValueForLotByReportColumn(ProjectPlanningDataSet.LotRow lot,
            ProjectPlanningDataSet.ReportColumnRow column)
        {
            bool hasChanged;
            return this.GetValueForLotByReportColumn(lot, column, null, out hasChanged);
        }

        /// <summary>
        /// Gibt die Label-Definition für eine Report-Column zurück.
        /// </summary>
        /// <param name="column"></param>
        /// <returns></returns>
        public ProjectPlanningDataSet.LabelRow GetLabelForReportColumn(ProjectPlanningDataSet.ReportColumnRow column)
        {
            return this.m_DataSet.Label.FindById(column.RelatedObjectId);
        }

        #endregion

        #region Private Implementation

        private string LoadLabelDataForLotAndColumn(ProjectPlanningDataSet.LotRow lot,
                                                    ProjectPlanningDataSet.ReportColumnRow reportColumn,
                                                    ProjectPlanningDataSet.UserRow user,
                                                    out bool hasChanged)
        {
            hasChanged = false;

            // Get the label definition
            var label = this.GetLabelForReportColumn(reportColumn);

            // Get the label value row for the lot
            var labelValue = this.m_DataSet.LabelValue.FirstOrDefault
                                (v => v.LotId == lot.Id && v.LabelId == label.Id);

            if (labelValue == null || labelValue.IsValueNull())
                return string.Empty;

            // Is this a user row? Should we resolve the user name?
            if (label.DataType == (byte)LabelDataType.User)
            {
                // is there a guid set in the value?
                if (labelValue.Value.IsValidGuid())
                {
                    var resolvedUser = this.m_DataSet.User.FindById(new Guid(labelValue.Value));
                    if (resolvedUser != null)
                        return resolvedUser.Username;
                    else
                        return string.Empty;
                }
            }

            if (user != null)
                hasChanged = this.m_ChangeLogModule.HasRowChangesByUser(labelValue, user);

            return labelValue.Value;
        }

        private string LoadMilestoneDataForLotAndColumn(ProjectPlanningDataSet.LotRow lot,
                                                        ProjectPlanningDataSet.ReportColumnRow reportColumn,
                                                        ProjectPlanningDataSet.UserRow user,
                                                        out bool hasChanged)
        {
            hasChanged = false;
            var milestoneData = this.GetMilestoneDataForLotAndColumn(lot, reportColumn);

            if (milestoneData == null)
                return string.Empty;

            string[] changedColumns = new string[0];
            if (user != null)
                changedColumns = this.m_ChangeLogModule.GetColumnNamesWithChanges(milestoneData, user);

            switch ((ReportColumnType)reportColumn.Type)
            {
                case ReportColumnType.MilestoneCompletion:
                    hasChanged = changedColumns.Contains("Completed");

                    if (milestoneData.IsCompletedNull())
                        return string.Empty;
                    else
                        return milestoneData.Completed.ToShortDateString();
                    break;

                case ReportColumnType.MilestoneInitialDate:
                    hasChanged = changedColumns.Contains("InitialDate");

                    if (milestoneData.IsInitialDateNull())
                        return string.Empty;
                    else
                        return milestoneData.InitialDate.ToShortDateString();
                    break;

                case ReportColumnType.MilestonePlanDate:
                    hasChanged = changedColumns.Contains("PlanDate");

                    if (milestoneData.IsPlanDateNull())
                        return string.Empty;
                    else
                        return milestoneData.PlanDate.ToShortDateString();
                    break;

                case ReportColumnType.MilestoneType:
                    hasChanged = changedColumns.Contains("Type");

                    if (milestoneData.IsTypeNull())
                        return string.Empty;
                    else
                        return milestoneData.Type;
                    break;
            }

            return string.Empty;
        }

        private ProjectPlanningDataSet.MilestoneDataRow GetMilestoneDataForLotAndColumn(
                                                            ProjectPlanningDataSet.LotRow lot,
                                                            ProjectPlanningDataSet.ReportColumnRow reportColumnRow)
        {
            // Get the milestone definition
            var milestone = this.m_DataSet.Milestone.FindById(reportColumnRow.RelatedObjectId);

            // Get the milestone data row for the lot
            var milestoneData = this.m_DataSet.MilestoneData.FirstOrDefault(
                                            m => m.LotId == lot.Id && m.MilestoneId == milestone.Id &&
                                                 m.DeliveryLotIndex == 0);
            return milestoneData;
        }

        #endregion
    }
}
